package it.univpm.diiga.memorysim.kernel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import it.univpm.diiga.memorysim.kernel.shared.EmptyBlock;
import it.univpm.diiga.memorysim.kernel.shared.Parameters;
import it.univpm.diiga.memorysim.kernel.shared.ProcessRequest;

public class MMU {
	
	private ProcessRequest process;
	private Hashtable<Integer,IMemoryElement> tableList;
	private PageFaultManage pfm;
	
	public MMU(){
		tableList = Parameters.getSOMemory();
		
		pfm = new PageFaultManage();

			
		
		EmptyBlock em = new EmptyBlock();
		em.setStart(0);
		em.setEnd(Parameters.getDimProcessMemory()-1);
		Parameters.getEmptyBlockMem().add(em);
		
		EmptyBlock em1 = new EmptyBlock();
		em1.setStart(0);
		em1.setEnd(Parameters.getDimSwap()-1);
		Parameters.getEmptyBlockSwap().add(em1);

	}
		
	public void init(ProcessRequest process){
		this.process=process;
		sortMemory(Parameters.getProcessMemory());
		sortMemory(Parameters.getSwap());
		sortMemory(Parameters.getEmptyBlockMem());
		sortMemory(Parameters.getEmptyBlockSwap());
		
		
		SegPageTable pt;
		
		if (!existTable(process.getPID())){
			System.out.println("---CARICO TUTTE LE PAGINE DELL'INTERO PROCESSO---");
			//Parameters.setStoryboard("Page Fault",process);
			pt=createTable(process);
			
			
			//Gestore page fault
			pfm.start(pt, process);						
			tableList.put(process.getPID(),pt);
			//System.out.println(Parameters.getSOMemory().get(process.getPID()));
			
		}else{
			 //System.out.println("Processo eseguito con successo");
			 pt=(SegPageTable) tableList.get(process.getPID());

			 if (pt.checkPage(process.getPagina())){
				 System.out.println("---PAGINA PRESENTE IN MEMORIA---");
				 Parameters.addEvent("Pagina presente",this.process);
			 }
			 else{
				 System.out.println("---PAGE FAULT---");
				 //Parameters.setStoryboard("Page Fault",process);
				 pfm.start(pt, process);	
			 }
		}
		//pt.writeRow(processRequest.getOffset());
		//System.out.println("Eseguito processo n "+process.getPID());
		
	}
	
	
	public SegPageTable createTable(ProcessRequest process){
		int pid=process.getPID();
		int dimTable=dimProcessTable(process);
		SegPageTable pt=new SegPageTable(process,dimTable);
		return pt;
	}
	
	private boolean existTable(int PID){
		if(tableList.containsKey(PID)){
				return true;
		}
		return false;
			
	}
	
	private int dimProcessTable(ProcessRequest proc){
		if(Parameters.getDimPage() == 0){
			List<ProcessRequest> pl=Parameters.getProcessList();
			int max=0;
			for (int i=0; i<pl.size();i++){
				if (pl.get(i).getPID()==proc.getPID() && max<=pl.get(i).getTableRow()){
					max=pl.get(i).getTableRow();
				}
			}
			
			return max+1;
		}
		else{
			int decimal = proc.getDimProcess()%Parameters.getDimPage();
			float dim = proc.getDimProcess()/Parameters.getDimPage();
			if(decimal == 0)
				return proc.getDimProcess()/Parameters.getDimPage();
			else
				return ((int)dim)+1;
		}

	}
	
	
	/*public void stampaTabella(){
		
		System.out.println(tableList.size());
	}*/
	
	private void sortMemory(List<IMemoryElement> elementsList){
		Collections.sort(elementsList, new Comparator<IMemoryElement>(){
			@Override
			public int compare(IMemoryElement proc1, IMemoryElement proc2) {
				if(proc1.getStart() < proc2.getStart()) return -1;
				if(proc1.getStart() > proc2.getStart()) return 1;
				return 0;
			}
		});
	}
	
	public void makeEmptyBlockList(){
		sortMemory(Parameters.getProcessMemory());
		sortMemory(Parameters.getSwap());
		sortMemory(Parameters.getEmptyBlockMem());
		sortMemory(Parameters.getEmptyBlockSwap());
		makeEmptyBlockListMem();
		makeEmptyBlockListSwap();
	}
	
	private void makeEmptyBlockListMem(){
		Parameters.getEmptyBlockMem().clear();
		List<IMemoryElement> elementsList = Parameters.getProcessMemory();
		IMemoryElement el1,el2;
		if(elementsList.size() > 0){
			int end1 = elementsList.get(0).getStart()-1;
			if(end1 >= 0){
				EmptyBlock em = new EmptyBlock(0,end1);
				Parameters.getEmptyBlockMem().add(em);
			}

			for(int i=0; i<elementsList.size()-1;i++){
				el1 = elementsList.get(i);
				el2 = elementsList.get(i+1);
				if(el1.getEnd() != el2.getStart()-1){
					System.out.println(el1.getEnd()+"----"+el2.getStart());
					Parameters.getEmptyBlockMem().add(i, new EmptyBlock(el1.getEnd()+1,el2.getStart()-1));
					System.out.println("Buco tra blocchi");
				}
				
			}
			int dimMemory=Parameters.getDimProcessMemory();
			int start =elementsList.get(elementsList.size()-1).getEnd()+1;
			if (start>=dimMemory)
				start=dimMemory-1;
			EmptyBlock em = new EmptyBlock(start,Parameters.getDimProcessMemory()-1);
			Parameters.getEmptyBlockMem().add(em);
			
			//Parameters.getEmptyBlockMem().get(Parameters.getEmptyBlockMem().size()-1).setStart(start);
		}
		else{

			EmptyBlock em = new EmptyBlock();
			em.setStart(0);
			em.setEnd(Parameters.getDimProcessMemory()-1);
			Parameters.getEmptyBlockMem().add(em);
			
			EmptyBlock em1 = new EmptyBlock();
			em1.setStart(0);
			em1.setEnd(Parameters.getDimSwap()-1);
			Parameters.getEmptyBlockSwap().add(em1);
		}
			
	}
	
	
	private void makeEmptyBlockListSwap(){
		Parameters.getEmptyBlockSwap().clear();
		List<IMemoryElement> elementsList = Parameters.getSwap();
		IMemoryElement el1,el2;
		
		if(elementsList.size() > 0){
			for(int i=0; i<elementsList.size()-1;i++){
				el1 = elementsList.get(i);
				el2 = elementsList.get(i+1);
				
				if(el1.getEnd() != el2.getStart()-1){
					Parameters.getEmptyBlockSwap().add(i, new EmptyBlock(el1.getEnd()+1,el2.getStart()-1));
				}
			}
			
			int dimSwap=Parameters.getDimSwap();
			int start = elementsList.get(elementsList.size()-1).getEnd()+1;
			if (start>=dimSwap)
				start=dimSwap-1;
			EmptyBlock em = new EmptyBlock(start,Parameters.getDimSwap()-1);
			Parameters.getEmptyBlockSwap().add(em);
		}
	}
}
